using System;
using System.Threading;
using System.Threading.Tasks;

namespace Nice157Advices.Advices
{
    /// <summary>
    /// 80.用Task 代替ThreadPool
    /// </summary>
    public static class _80
    {
        /**
         * ThreadPool 在使用上存在一定的不便之处：
         * - 不支持线程的取消、完成、失败通知等交互性操作
         * - 不支持线程执行的先后次序
         * 
         * 因此提供了一个功能更强大的概览：Task
         * Task 在线程池的基础上进行了优化，并提供了更多的API
         */

        public static void Go ()
        {
            // Test1 ();   // 任务完成
            // Test2 ();   // 任务完成
            // Test3 ();   // 因为取消而完成 任务完成
            // Test4 ();   // 因为异常而完成 任务完成
            Test5();    // Task还支持任务工厂的概念，支持多个任务共享相同的状态，如取消 CancellationTokenSource 就是可以被共享的。通过使用任务工厂，可以同时取消一组任务。
        }

        private static void Test1 ()
        {
            Task t = new Task (() =>
            {
                System.Console.WriteLine ("task beginning");
                Thread.Sleep (5000);
            });
            t.Start ();

            // ContinueWith方法可以在一个任务完成的时候发起一个新任务，因此可以在新任务中获取原任务的结果值
            t.ContinueWith ((task) =>
            {
                /**
                 * - IsCanceled     因为被取消而完成
                 * - IsCompleted    成功完成
                 * - IsFaulted      因为发生异常而完成
                 */

                System.Console.WriteLine ("task is done:");
                System.Console.WriteLine ($"IsCanceled={task.IsCanceled}\tIsCompleted={task.IsCompleted}\tIsFaulted={task.IsFaulted}");
            });
            System.Console.ReadKey ();
        }

        private static void Test2 ()
        {
            CancellationTokenSource cts = new CancellationTokenSource ();
            Task<int> t = new Task<int> (() => Add (cts.Token), cts.Token);
            t.Start ();
            t.ContinueWith (TaskEnded);
            System.Console.ReadKey ();
            cts.Cancel ();
            System.Console.ReadKey ();
        }

        private static void TaskEnded (Task<int> task)
        {
            System.Console.WriteLine ("task was done");
            System.Console.WriteLine ($"IsCanceled={task.IsCanceled}\tIsCompleted={task.IsCompleted}\tIsFaulted={task.IsFaulted}");
            System.Console.WriteLine ($"task return value: {task.Result}");
        }

        private static int Add (CancellationToken ct)
        {
            System.Console.WriteLine ("task began");
            int result = 0;
            while (!ct.IsCancellationRequested)
            {
                result++;
                Thread.Sleep (1000);
            }
            return result;
        }

        private static void Test3 ()
        {
            CancellationTokenSource cts = new CancellationTokenSource ();
            Task<int> t = new Task<int> (() => AddCancelByThrow (cts.Token), cts.Token);
            t.Start ();
            t.ContinueWith (TaskEndedByCatch);
            System.Console.ReadKey ();
            cts.Cancel ();
            System.Console.ReadKey ();
        }

        private static void TaskEndedByCatch (Task<int> task)
        {
            System.Console.WriteLine ("task was done");
            System.Console.WriteLine ($"IsCanceled={task.IsCanceled}\tIsCompleted={task.IsCompleted}\tIsFaulted={task.IsFaulted}");
            try
            {
                System.Console.WriteLine ($"task return value: {task.Result}");
            }
            catch (AggregateException ex)
            {
                System.Console.WriteLine ("exception!");
                ex.Handle ((err) => err is OperationCanceledException);
            }
        }

        private static int AddCancelByThrow (CancellationToken ct)
        {
            System.Console.WriteLine ("task began");
            int result = 0;
            while (true)
            {
                // 通过 ThrowIfCancellationRequested() 取消的任务不算“因为异常而完成”
                // 因为 ThrowIfCancellationRequested 时协作式取消方式的类型 CancellationTokenSource 中的一个方法，CLR 对其进行了特殊处理，CLR知道这一行是开发者有意为之，所以不把它看作一个异常。
                ct.ThrowIfCancellationRequested ();
                result++;
                Thread.Sleep (1000);
            }
            return result;
        }

        private static void Test4 ()
        {
            CancellationTokenSource cts = new CancellationTokenSource ();
            Task<int> t = new Task<int> (() => AddThrow (cts.Token), cts.Token);
            t.Start ();
            t.ContinueWith (TaskEndedByCatch);
            System.Console.ReadKey ();
            cts.Cancel ();
            System.Console.ReadKey ();
        }

        private static int AddThrow (CancellationToken ct)
        {
            System.Console.WriteLine ("task began");
            int result = 0;
            while (true)
            {
                if (result == 5)
                    throw new Exception ("error");
                result++;
                Thread.Sleep (1000);
            }
            return result;
        }

        private static void Test5() {
            CancellationTokenSource cts = new CancellationTokenSource();
            TaskFactory taskFactory = new TaskFactory();
            Task[] tasks = new Task[] {
                taskFactory.StartNew(() => Add(cts.Token)),
                taskFactory.StartNew(() => Add(cts.Token)),
                taskFactory.StartNew(() => Add(cts.Token)),
            };

            // CancellationToken.None 指示 TasksEnded 不能被取消
            taskFactory.ContinueWhenAll(tasks, TasksEnded, CancellationToken.None);
            System.Console.ReadKey();
            cts.Cancel();
            System.Console.ReadKey();
        }

        private static void TasksEnded(Task[] tasks) {
            System.Console.WriteLine("all tasks were done!");
        }
    }
}